﻿
#include "ScriptMgr.h"
#include "Creature.h"
#include "GameObject.h"
#include "InstanceScript.h"
#include "karazhan.h"
#include "SpellScript.h"
#include "SpellAuraEffects.h"
#include "Map.h"
#include "Player.h"

const Position OptionalSpawn[] =
{
    { -10960.981445f, -1940.138428f, 46.178097f, 4.12f }, // Hyakiss the Lurker
    { -10945.769531f, -2040.153320f, 49.474438f, 0.077f }, // Shadikith the Glider
    { -10899.903320f, -2085.573730f, 49.474449f, 1.38f }  // Rokad the Ravager
};

enum eSpells
{
    // Chess event spells
    SPELL_CLEAR_BOARD = 37366,                    // spell cast to clear the board at the end of the event
    SPELL_GAME_IN_SESSION = 39331,                    // debuff on players received while the game is in session
    SPELL_FORCE_KILL_BUNNY = 45260,                    // triggers 45259
    SPELL_GAME_OVER = 39401,                    // cast by Medivh on game end
    SPELL_VICTORY_VISUAL = 39395,                    // cast by the Victory controller on game end

};

void instance_karazhan::instance_karazhan_InstanceMapScript::OnCreatureCreate(Creature* creature)
{
    switch (creature->GetEntry())
    {
                case NPC_KILREK:
                    m_uiKilrekGUID = creature->GetGUID();
                    break;
                case NPC_TERESTIAN_ILLHOOF:
                    m_uiTerestianGUID = creature->GetGUID();
                    break;
                case NPC_MOROES:
                    m_uiMoroesGUID = creature->GetGUID();
                    break;
                case NPC_NIGHTBANE:
                    m_uiNightBaneGUID = creature->GetGUID();
                    break;
                case NPC_RELAY:
                    m_uiRelayGUID = creature->GetGUID();
                    break;
    //-- chess --
    case NPC_HUMAN_CHARGER:
    case NPC_HUMAN_CLERIC:
    case NPC_HUMAN_CONJURER:
    case NPC_HUMAN_FOOTMAN:
    case NPC_CONJURED_WATER_ELEMENTAL:
    case NPC_KING_LLANE:
        m_lChessPiecesAlliance.push_back(creature->GetGUID());
        break;
    case NPC_ORC_GRUNT:
    case NPC_ORC_NECROLYTE:
    case NPC_ORC_WARLOCK:
    case NPC_ORC_WOLF:
    case NPC_SUMMONED_DAEMON:
    case NPC_WARCHIEF_BLACKHAND:
        m_lChessPiecesHorde.push_back(creature->GetGUID());
        break;
    case NPC_NIGHTBANE_HELPER:
        if (creature->GetPositionZ() < 100.0f)
            m_lNightbaneGroundTriggers.push_back(creature->GetGUID());
        else
            m_lNightbaneAirTriggers.push_back(creature->GetGUID());
        break;
    case NPC_INVISIBLE_STALKER:
        if (creature->GetPositionY() < -1870.0f)
            m_lChessHordeStalkerList.push_back(creature->GetGUID());
        else
            m_lChessAllianceStalkerList.push_back(creature->GetGUID());
        break;
    case NPC_CHESS_STATUS_BAR:
        if (creature->GetPositionY() < -1870.0f)
            m_HordeStatusGuid = creature->GetGUID();
        else
            m_AllianceStatusGuid = creature->GetGUID();
        break;
    case NPC_ECHO_MEDIVH:
        m_uiMedivhGUID = creature->GetGUID();
        break;
    case NPC_CHESS_VICTORY_CONTROLLER:
        m_uiChessVicConGUID = creature->GetGUID();
        //m_mNpcEntryGuidStore[creature->GetEntry()] = creature->GetGUID();
        break;
    }
}

void instance_karazhan::instance_karazhan_InstanceMapScript::Initialize()
{
    SetBossNumber(EncounterCount);

    // 1 - OZ, 2 - HOOD, 3 - RAJ, this never gets altered.
    OperaEvent = urand(EVENT_OZ, EVENT_RAJ);
    OzDeathCount = 0;
    OptionalBossCount = 0;

    m_uiTeam = 0;
    m_uiChessResetTimer = 0;
    m_uiAllianceStalkerCount = 0;
    m_uiHordeStalkerCount = 0;
    m_bFriendlyGame = false;

    m_uiCurtainGUID = 0;
    m_uiStageDoorLeftGUID = 0;
    m_uiStageDoorRightGUID = 0;

    m_uiKilrekGUID = 0;
    m_uiTerestianGUID = 0;
    m_uiMoroesGUID = 0;

    m_uiLibraryDoor = 0;
    m_uiMassiveDoor = 0;
    m_uiSideEntranceDoor = 0;
    m_uiGamesmansDoor = 0;
    m_uiGamesmansExitDoor = 0;
    m_uiNetherspaceDoor = 0;
    MastersTerraceDoor[0] = 0;
    MastersTerraceDoor[1] = 0;
    ImageGUID = 0;
    DustCoveredChest = 0;

    m_uiMedivhGUID = 0;
    m_uiChessVicConGUID = 0;
    m_ChessState = 0;

    m_uiChessDamages = 0;
}

void instance_karazhan::instance_karazhan_InstanceMapScript::OnUnitDeath(Unit* unit)
{
    Creature* creature = unit->ToCreature();
    if (!creature)
        return;

    switch (creature->GetEntry())
    {
                case NPC_COLDMIST_WIDOW:
                case NPC_COLDMIST_STALKER:
                case NPC_SHADOWBAT:
                case NPC_VAMPIRIC_SHADOWBAT:
                case NPC_GREATER_SHADOWBAT:
                case NPC_PHASE_HOUND:
                case NPC_DREADBEAST:
                case NPC_SHADOWBEAST:
                    if (GetBossState(DATA_OPTIONAL_BOSS) == TO_BE_DECIDED)
                    {
                        ++OptionalBossCount;
                        if (OptionalBossCount == OPTIONAL_BOSS_REQUIRED_DEATH_COUNT)
                        {
                            switch (urand(NPC_HYAKISS_THE_LURKER, NPC_ROKAD_THE_RAVAGER))
                            {
                                case NPC_HYAKISS_THE_LURKER:
                                    instance->SummonCreature(NPC_HYAKISS_THE_LURKER, OptionalSpawn[0]);
                                    break;
                                case NPC_SHADIKITH_THE_GLIDER:
                                    instance->SummonCreature(NPC_SHADIKITH_THE_GLIDER, OptionalSpawn[1]);
                                    break;
                                case NPC_ROKAD_THE_RAVAGER:
                                    instance->SummonCreature(NPC_ROKAD_THE_RAVAGER, OptionalSpawn[2]);
                                    break;
                            }
                        }
                    }
                    break;
                default:
                    break;
    }
}

void instance_karazhan::instance_karazhan_InstanceMapScript::SetData(uint32 type, uint32 data)
{
    switch (type)
    {
                case DATA_OPERA_OZ_DEATHCOUNT:
                    if (data == SPECIAL)
                        ++OzDeathCount;
                    else if (data == IN_PROGRESS)
                        OzDeathCount = 0;
                    break;

    case DATA_CHESS:
    {
        if (data == DONE)
        {
            // doors and loot are not handled for friendly games
            if (GetData(DATA_CHESS) != SPECIAL)
            {
                //                            DoUseDoorOrButton(GO_GAMESMANS_HALL_EXIT_DOOR);
                DoRespawnGameObject(DustCoveredChest, DAY);
                if (GameObject* loot = instance->GetGameObject(DustCoveredChest))
                    loot->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
            }

            // cast game end spells
            if (Creature* pMedivh = instance->GetCreature(GetData64(NPC_ECHO_MEDIVH)))
            {
                pMedivh->CastSpell(pMedivh, SPELL_FORCE_KILL_BUNNY, true);
                pMedivh->CastSpell(pMedivh, SPELL_GAME_OVER, true);
                pMedivh->CastSpell(pMedivh, SPELL_CLEAR_BOARD, true);
            }
            if (Creature* pController = instance->GetCreature(GetData64(NPC_CHESS_VICTORY_CONTROLLER)))
                pController->CastSpell(pController, SPELL_VICTORY_VISUAL, true);
            DoRemoveAurasDueToSpellOnPlayers(SPELL_GAME_IN_SESSION);

            m_bFriendlyGame = false;
            m_uiChessResetTimer = 35000;
        }
        else if (data == FAIL)
        {
            // clean the board for reset
            if (Creature* pMedivh = instance->GetCreature(GetData64(NPC_ECHO_MEDIVH)))
            {
                pMedivh->CastSpell(pMedivh, SPELL_GAME_OVER, true);
                pMedivh->CastSpell(pMedivh, SPELL_CLEAR_BOARD, true);
            }
            DoRemoveAurasDueToSpellOnPlayers(SPELL_GAME_IN_SESSION);
            m_uiChessResetTimer = 35000;
        }
        else if (data == IN_PROGRESS || data == SPECIAL)
            DoPrepareChessEvent();
            m_ChessState = data;
        break;
    }
    case DATA_CHESS_DAMAGE:
        m_uiChessDamages += data;
        break;
    }

    /*
    if (data == DONE)
    {
        OUT_SAVE_INST_DATA;

        std::ostringstream saveStream;
        saveStream << m_auiEncounter[0] << ' ' << m_auiEncounter[1] << ' ' << m_auiEncounter[2] << ' '
            << m_auiEncounter[3] << ' ' << m_auiEncounter[4] << ' ' << m_auiEncounter[5] << ' ' << m_auiEncounter[6] << ' '
            << m_auiEncounter[7] << ' ' << m_auiEncounter[8] << ' ' << m_auiEncounter[9] << ' ' << m_auiEncounter[10] << ' ' << m_auiEncounter[11];

        strSaveData = saveStream.str();

        SaveToDB();
        OUT_SAVE_INST_DATA_COMPLETE;
    }
    */
}

bool instance_karazhan::instance_karazhan_InstanceMapScript::SetBossState(uint32 type, EncounterState state)
{
    if (!InstanceScript::SetBossState(type, state))
        return false;

    switch (type)
    {
                case DATA_OPERA_PERFORMANCE:
                    if (state == DONE)
                    {
                        HandleGameObject(m_uiStageDoorLeftGUID, true);
                        HandleGameObject(m_uiStageDoorRightGUID, true);
                        if (GameObject* sideEntrance = instance->GetGameObject(m_uiSideEntranceDoor))
                            sideEntrance->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
                        instance->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, 16812, nullptr);
                    }
                    break;
                case DATA_CHESS:
                    if (state == DONE)
                        DoRespawnGameObject(DustCoveredChest, DAY);
                    break;
                default:
                    break;
    }

    return true;
}

void instance_karazhan::instance_karazhan_InstanceMapScript::SetData64(uint32 type, uint64 data)
{
    if (type == DATA_IMAGE_OF_MEDIVH)
        ImageGUID = data;
}

void instance_karazhan::instance_karazhan_InstanceMapScript::OnGameObjectCreate(GameObject* go)
{
    switch (go->GetEntry())
    {
                case GO_STAGE_CURTAIN:
                    m_uiCurtainGUID = go->GetGUID();
                    break;
                case GO_STAGE_DOOR_LEFT:
                    m_uiStageDoorLeftGUID = go->GetGUID();
                    if (GetBossState(DATA_OPERA_PERFORMANCE) == DONE)
                        go->SetGoState(GO_STATE_ACTIVE);
                    break;
                case GO_STAGE_DOOR_RIGHT:
                    m_uiStageDoorRightGUID = go->GetGUID();
                    if (GetBossState(DATA_OPERA_PERFORMANCE) == DONE)
                        go->SetGoState(GO_STATE_ACTIVE);
                    break;
                case GO_PRIVATE_LIBRARY_DOOR:
                    m_uiLibraryDoor = go->GetGUID();
                    break;
                case GO_MASSIVE_DOOR:
                    m_uiMassiveDoor = go->GetGUID();
                    if (GetBossState(DATA_ARAN) != IN_PROGRESS)
                        go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
                    else
                        go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
                    break;
                case GO_GAMESMAN_HALL_DOOR:
                    m_uiGamesmansDoor = go->GetGUID();
                    break;
                case GO_GAMESMAN_HALL_EXIT_DOOR:
                    m_uiGamesmansExitDoor = go->GetGUID();
                    break;
                case GO_NETHERSPACE_DOOR:
                    m_uiNetherspaceDoor = go->GetGUID();
                    if (GetBossState(DATA_PRINCE) != IN_PROGRESS)
                        go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
                    else
                        go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
                    break;
                case GO_MASTERS_TERRACE_DOOR:
                    MastersTerraceDoor[0] = go->GetGUID();
                    break;
                case GO_MASTERS_TERRACE_DOOR2:
                    MastersTerraceDoor[1] = go->GetGUID();
                    break;
                case GO_SIDE_ENTRANCE_DOOR:
                    m_uiSideEntranceDoor = go->GetGUID();
                    if (GetBossState(DATA_OPERA_PERFORMANCE) == DONE)
                        go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
                    else
                        go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
                    break;
                case GO_DUST_COVERED_CHEST:
                    DustCoveredChest = go->GetGUID();
                    break;
    }

    switch (OperaEvent)
    {
        /// @todo Set Object visibilities for Opera based on performance
                case EVENT_OZ:
                    break;

                case EVENT_HOOD:
                    break;

                case EVENT_RAJ:
                    break;
    }
}

uint32 instance_karazhan::instance_karazhan_InstanceMapScript::GetData(uint32 type) const
{
    switch (type)
    {
                case DATA_OPERA_PERFORMANCE:
                    return OperaEvent;
                case DATA_OPERA_OZ_DEATHCOUNT:
                    return OzDeathCount;

                case DATA_KILREK:
                    return m_uiKilrekGUID;
                case DATA_TERESTIAN:
                    return m_uiTerestianGUID;
                case DATA_MOROES:
                    return m_uiMoroesGUID;
                case DATA_GO_STAGEDOORLEFT:
                    return m_uiStageDoorLeftGUID;
                case DATA_GO_STAGEDOORRIGHT:
                    return m_uiStageDoorRightGUID;
                case DATA_GO_CURTAINS:
                    return m_uiCurtainGUID;
                case DATA_GO_LIBRARY_DOOR:
                    return m_uiLibraryDoor;
                case DATA_GO_MASSIVE_DOOR:
                    return m_uiMassiveDoor;
                case DATA_GO_SIDE_ENTRANCE_DOOR:
                    return m_uiSideEntranceDoor;
                case DATA_GO_GAME_DOOR:
                    return m_uiGamesmansDoor;
                case DATA_GO_GAME_EXIT_DOOR:
                    return m_uiGamesmansExitDoor;
                case DATA_GO_NETHER_DOOR:
                    return m_uiNetherspaceDoor;
                case DATA_MASTERS_TERRACE_DOOR_1:
                    return MastersTerraceDoor[0];
                case DATA_MASTERS_TERRACE_DOOR_2:
                    return MastersTerraceDoor[1];
                case DATA_IMAGE_OF_MEDIVH:
                    return ImageGUID;
    case DATA_TEAM_IN_INSTANCE:
        return m_uiTeam;
    case DATA_CHESS_DAMAGE:
        return m_uiChessDamages;
    case DATA_CHESS:
        return m_ChessState;
    }

    return 0;
}

uint64 instance_karazhan::instance_karazhan_InstanceMapScript::GetData64(uint32 data) const
{
    switch (data)
    {
        case DATA_KILREK:
            return m_uiKilrekGUID;
        case DATA_TERESTIAN:
            return m_uiTerestianGUID;
        case DATA_MOROES:
            return m_uiMoroesGUID;
        case DATA_GO_STAGEDOORLEFT:
            return m_uiStageDoorLeftGUID;
        case DATA_GO_STAGEDOORRIGHT:
            return m_uiStageDoorRightGUID;
        case DATA_GO_CURTAINS:
            return m_uiCurtainGUID;
        case DATA_GO_LIBRARY_DOOR:
            return m_uiLibraryDoor;
        case DATA_GO_MASSIVE_DOOR:
            return m_uiMassiveDoor;
        case DATA_GO_SIDE_ENTRANCE_DOOR:
            return m_uiSideEntranceDoor;
        case DATA_GO_GAME_DOOR:
            return m_uiGamesmansDoor;
        case DATA_GO_GAME_EXIT_DOOR:
            return m_uiGamesmansExitDoor;
        case DATA_GO_NETHER_DOOR:
            return m_uiNetherspaceDoor;
        case DATA_MASTERS_TERRACE_DOOR_1:
            return MastersTerraceDoor[0];
        case DATA_MASTERS_TERRACE_DOOR_2:
            return MastersTerraceDoor[1];
        case DATA_IMAGE_OF_MEDIVH:
            return ImageGUID;
        case DATA_NIGHTBANE:
            return m_uiNightBaneGUID;
        case NPC_ECHO_MEDIVH:
            return m_uiMedivhGUID;
        case NPC_CHESS_VICTORY_CONTROLLER:
            return m_uiChessVicConGUID;
    }

    return 0;
}

void instance_karazhan::instance_karazhan_InstanceMapScript::OnPlayerEnter(Player* player)
{
    if (!m_uiTeam)
        m_uiTeam = player->GetTeamId();
}

bool instance_karazhan::instance_karazhan_InstanceMapScript::IsFriendlyGameReady()
{
    return m_bFriendlyGame;
}

bool instance_karazhan::instance_karazhan_InstanceMapScript::IsCaseOccupied(Creature* creature)
{
    for (std::list<uint64 >::const_iterator itr = m_lChessPiecesAlliance.begin(); itr != m_lChessPiecesAlliance.end(); ++itr)
        if (Creature* pChessPiece = instance->GetCreature(*itr))
            if (pChessPiece->IsAlive())
                if (pChessPiece->AI()->GetGUID() == creature->GetGUID())
                    return true;

    for (std::list<uint64 >::const_iterator itr = m_lChessPiecesHorde.begin(); itr != m_lChessPiecesHorde.end(); ++itr)
        if (Creature* pChessPiece = instance->GetCreature(*itr))
            if (pChessPiece->IsAlive())
                if (pChessPiece->AI()->GetGUID() == creature->GetGUID())
                    return true;
    return false;
}

void instance_karazhan::instance_karazhan_InstanceMapScript::DoMoveChessPieceToSides(uint32 uiSpellId, uint32 uiFaction, bool bGameEnd)
{
    // assign proper faction variables
    std::vector<uint64 >& vStalkers = uiFaction == FACTION_ID_CHESS_ALLIANCE ? m_vAllianceStalkers : m_vHordeStalkers;
    uint32 uiCount = uiFaction == FACTION_ID_CHESS_ALLIANCE ? m_uiAllianceStalkerCount : m_uiHordeStalkerCount;

    // get the proper statusBar npc
    Creature* pStatusBar = instance->GetCreature(uiFaction == FACTION_ID_CHESS_ALLIANCE ? m_AllianceStatusGuid : m_HordeStatusGuid);
    if (!pStatusBar)
        return;

    if (vStalkers.size() < uiCount + 1)
        return;

    // handle stalker transformation
    if (Creature* pStalker = instance->GetCreature(vStalkers[uiCount]))
    {
        // need to provide specific target, in order to ensure the logic of the event
        pStatusBar->CastSpell(pStalker, uiSpellId, true);
        uiFaction == FACTION_ID_CHESS_ALLIANCE ? ++m_uiAllianceStalkerCount : ++m_uiHordeStalkerCount;
    }

    // handle emote on end game
    if (bGameEnd)
    {
        // inverse factions
        vStalkers.clear();
        vStalkers = uiFaction == FACTION_ID_CHESS_ALLIANCE ? m_vHordeStalkers : m_vAllianceStalkers;

        for (std::vector<uint64 >::const_iterator itr = vStalkers.begin(); itr != vStalkers.end(); ++itr)
        {
            if (Creature* pStalker = instance->GetCreature(*itr))
                pStalker->HandleEmoteCommand(EMOTE_STATE_APPLAUD);
        }
    }
}

void instance_karazhan::instance_karazhan_InstanceMapScript::DoPrepareChessEvent()
{
    m_uiChessDamages = 0;

    if (Creature* pMedivh = instance->GetCreature(GetData64(NPC_ECHO_MEDIVH)))
        pMedivh->AI()->DoAction(AI_EVENT_CUSTOM_B);

    // Allow all the chess pieces to init start position
    for (std::list<uint64 >::const_iterator itr = m_lChessPiecesAlliance.begin(); itr != m_lChessPiecesAlliance.end(); ++itr)
        if (Creature* pChessPiece = instance->GetCreature(*itr))
        {
            Creature* pSquare = GetClosestCreatureWithEntry(pChessPiece, NPC_SQUARE_BLACK, 2.0f);
            if (!pSquare)
                pSquare = GetClosestCreatureWithEntry(pChessPiece, NPC_SQUARE_WHITE, 2.0f);
            else
            {
                Creature* pSquare2 = GetClosestCreatureWithEntry(pChessPiece, NPC_SQUARE_WHITE, 2.0f);
                if (pSquare2)
                    if (pChessPiece->GetExactDist2d(pSquare2->GetPositionX(), pSquare2->GetPositionY()) < pChessPiece->GetExactDist2d(pSquare->GetPositionX(), pSquare->GetPositionY()))
                        pSquare = pSquare2;
            }
            if (!pSquare)
                return;

            // send event which will prepare the current square
            pChessPiece->AI()->SetGUID(pSquare->GetGUID());
            pChessPiece->AI()->DoAction(AI_EVENT_CUSTOM_B);
        }

    for (std::list<uint64 >::const_iterator itr = m_lChessPiecesHorde.begin(); itr != m_lChessPiecesHorde.end(); ++itr)
        if (Creature* pChessPiece = instance->GetCreature(*itr))
        {
            Creature* pSquare = GetClosestCreatureWithEntry(pChessPiece, NPC_SQUARE_BLACK, 2.0f);
            if (!pSquare)
                pSquare = GetClosestCreatureWithEntry(pChessPiece, NPC_SQUARE_WHITE, 2.0f);
            else
            {
                Creature* pSquare2 = GetClosestCreatureWithEntry(pChessPiece, NPC_SQUARE_WHITE, 2.0f);
                if (pSquare2)
                    if (pChessPiece->GetExactDist2d(pSquare2->GetPositionX(), pSquare2->GetPositionY()) < pChessPiece->GetExactDist2d(pSquare->GetPositionX(), pSquare->GetPositionY()))
                        pSquare = pSquare2;
            }

            if (!pSquare)
                return;

            // send event which will prepare the current square
            pChessPiece->AI()->SetGUID(pSquare->GetGUID());
            pChessPiece->AI()->DoAction(AI_EVENT_CUSTOM_B);
        }

    // add silence debuff
    DoCastSpellOnPlayers(SPELL_GAME_IN_SESSION);
    m_uiAllianceStalkerCount = 0;
    m_uiHordeStalkerCount = 0;
    m_vHordeStalkers.clear();
    m_vAllianceStalkers.clear();

    // sort stalkers depending on side
    std::list<Creature*> lStalkers;
    for (std::list<uint64 >::const_iterator itr = m_lChessHordeStalkerList.begin(); itr != m_lChessHordeStalkerList.end(); ++itr)
        if (Creature* pTemp = instance->GetCreature(*itr))
            lStalkers.push_back(pTemp);

    if (lStalkers.empty())
        return;

    // get the proper statusBar npc
    Creature* pStatusBar = instance->GetCreature(m_HordeStatusGuid);
    if (!pStatusBar)
        return;

    lStalkers.sort(acore::ObjectDistanceOrderPred(pStatusBar));
    for (std::list<Creature*>::const_iterator itr = lStalkers.begin(); itr != lStalkers.end(); ++itr)
        m_vHordeStalkers.push_back((*itr)->GetGUID());

    lStalkers.clear();
    for (std::list<uint64 >::const_iterator itr = m_lChessAllianceStalkerList.begin(); itr != m_lChessAllianceStalkerList.end(); ++itr)
        if (Creature* pTemp = instance->GetCreature(*itr))
            lStalkers.push_back(pTemp);

    if (lStalkers.empty())
        return;

    // get the proper statusBar npc
    pStatusBar = instance->GetCreature(m_AllianceStatusGuid);
    if (!pStatusBar)
        return;

    lStalkers.sort(acore::ObjectDistanceOrderPred(pStatusBar));
    for (std::list<Creature*>::const_iterator itr = lStalkers.begin(); itr != lStalkers.end(); ++itr)
        m_vAllianceStalkers.push_back((*itr)->GetGUID());
}

void instance_karazhan::instance_karazhan_InstanceMapScript::Update(uint32 uiDiff)
{
    if (m_uiChessResetTimer)
    {
        // respawn all chess pieces and side stalkers on the original position
        if (m_uiChessResetTimer <= uiDiff)
        {
            for (std::list<uint64 >::const_iterator itr = m_lChessPiecesAlliance.begin(); itr != m_lChessPiecesAlliance.end(); ++itr)
            {
                if (Creature* pTemp = instance->GetCreature(*itr))
                {
                    if (!pTemp->IsAlive())
                        pTemp->Respawn();
                    else
                    {
                        float x, y, z, o;
                        pTemp->GetHomePosition(x, y, z, o);
                        pTemp->NearTeleportTo(x, y, z, o);
                    }
                    pTemp->SetVisible(true);
                }
            }
            for (std::list<uint64 >::const_iterator itr = m_lChessPiecesHorde.begin(); itr != m_lChessPiecesHorde.end(); ++itr)
            {
                if (Creature* pTemp = instance->GetCreature(*itr))
                {
                    if (!pTemp->IsAlive())
                        pTemp->Respawn();
                    else
                    {
                        float x, y, z, o;
                        pTemp->GetHomePosition(x, y, z, o);
                        pTemp->NearTeleportTo(x, y, z, o);
                    }
                    pTemp->SetVisible(true);
                }
            }

            for (std::list<uint64 >::const_iterator itr = m_lChessAllianceStalkerList.begin(); itr != m_lChessAllianceStalkerList.end(); ++itr)
            {
                if (Creature* pTemp = instance->GetCreature(*itr))
                {
                    if (!pTemp->IsAlive())
                        pTemp->Respawn();
                    else
                    {
                        float x, y, z, o;
                        pTemp->GetHomePosition(x, y, z, o);
                        pTemp->NearTeleportTo(x, y, z, o);
                    }
                    pTemp->SetVisible(true);
                    pTemp->HandleEmoteCommand(EMOTE_STATE_NONE);
                }
            }
            for (std::list<uint64 >::const_iterator itr = m_lChessHordeStalkerList.begin(); itr != m_lChessHordeStalkerList.end(); ++itr)
            {
                if (Creature* pTemp = instance->GetCreature(*itr))
                {
                    if (!pTemp->IsAlive())
                        pTemp->Respawn();
                    else
                    {
                        float x, y, z, o;
                        pTemp->GetHomePosition(x, y, z, o);
                        pTemp->NearTeleportTo(x, y, z, o);
                    }
                    pTemp->SetVisible(true);
                    pTemp->HandleEmoteCommand(EMOTE_STATE_NONE);
                }
            }

            for (std::vector<uint64 >::const_iterator itr = m_vHordeStalkers.begin(); itr != m_vHordeStalkers.end(); ++itr)
                if (Creature* pStalker = instance->GetCreature(*itr))
                    pStalker->RemoveAllAuras();

            for (std::vector<uint64 >::const_iterator itr = m_vAllianceStalkers.begin(); itr != m_vAllianceStalkers.end(); ++itr)
                if (Creature* pStalker = instance->GetCreature(*itr))
                    pStalker->RemoveAllAuras();

            if (GetData(DATA_CHESS) == FAIL || GetData(DATA_CHESS) == IN_PROGRESS)
                SetData(DATA_CHESS, NOT_STARTED);
            else if (GetData(DATA_CHESS) == DONE)
                m_bFriendlyGame = true;

            m_uiChessDamages = 0;
            m_uiChessResetTimer = 0;
            if (Creature* pMedivh = instance->GetCreature(GetData64(NPC_ECHO_MEDIVH)))
                pMedivh->AI()->Reset();
        }
        else
            m_uiChessResetTimer -= uiDiff;
    }
}

std::string instance_karazhan::instance_karazhan_InstanceMapScript::GetSaveData()
{
    return strSaveData;
}

void instance_karazhan::instance_karazhan_InstanceMapScript::Load(char const* chrIn)
{
    /*
    if (!chrIn)
    {
        OUT_LOAD_INST_DATA_FAIL;
        return;
    }

    OUT_LOAD_INST_DATA(chrIn);
    std::istringstream loadStream(chrIn);

    loadStream >> m_auiEncounter[0] >> m_auiEncounter[1] >> m_auiEncounter[2] >> m_auiEncounter[3]
        >> m_auiEncounter[4] >> m_auiEncounter[5] >> m_auiEncounter[6] >> m_auiEncounter[7]
        >> m_auiEncounter[8] >> m_auiEncounter[9] >> m_auiEncounter[10] >> m_auiEncounter[11];
    for (uint8 i = 0; i < MAX_ENCOUNTERS; ++i)
        if (m_auiEncounter[i] == IN_PROGRESS)                // Do not load an encounter as "In Progress" - reset it instead.
            m_auiEncounter[i] = NOT_STARTED;
    OUT_LOAD_INST_DATA_COMPLETE;
    */
}


class spell_karazhan_brittle_bones : public SpellScriptLoader
{
public:
    spell_karazhan_brittle_bones() : SpellScriptLoader("spell_karazhan_brittle_bones") { }

    class spell_karazhan_brittle_bones_AuraScript : public AuraScript
    {
        PrepareAuraScript(spell_karazhan_brittle_bones_AuraScript);

        void CalcPeriodic(AuraEffect const* /*effect*/, bool& isPeriodic, int32& amplitude)
        {
            isPeriodic = true;
            amplitude = 5000;
        }

        void Update(AuraEffect const*  /*effect*/)
        {
            PreventDefaultAction();
            if (roll_chance_i(35))
                GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_RATTLED, true);
        }

        void Register()
        {
            DoEffectCalcPeriodic += AuraEffectCalcPeriodicFn(spell_karazhan_brittle_bones_AuraScript::CalcPeriodic, EFFECT_0, SPELL_AURA_DUMMY);
            OnEffectPeriodic += AuraEffectPeriodicFn(spell_karazhan_brittle_bones_AuraScript::Update, EFFECT_0, SPELL_AURA_DUMMY);
        }
    };

    AuraScript* GetAuraScript() const
    {
        return new spell_karazhan_brittle_bones_AuraScript();
    }
};

class spell_karazhan_overload : public SpellScriptLoader
{
public:
    spell_karazhan_overload() : SpellScriptLoader("spell_karazhan_overload") { }

    class spell_karazhan_overload_AuraScript : public AuraScript
    {
        PrepareAuraScript(spell_karazhan_overload_AuraScript);

        void PeriodicTick(AuraEffect const* auraEffect)
        {
            PreventDefaultAction();
            //Should stop at 3200 damage, maybe check needed(?)
            GetUnitOwner()->CastCustomSpell(SPELL_OVERLOAD, SPELLVALUE_BASE_POINT0, int32(auraEffect->GetAmount() * pow(2.0, auraEffect->GetTickNumber())), GetUnitOwner(), true);
        }

        void Register()
        {
            OnEffectPeriodic += AuraEffectPeriodicFn(spell_karazhan_overload_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
        }
    };

    AuraScript* GetAuraScript() const
    {
        return new spell_karazhan_overload_AuraScript();
    }
};

class spell_karazhan_blink : public SpellScriptLoader
{
public:
    spell_karazhan_blink() : SpellScriptLoader("spell_karazhan_blink") { }

    class spell_karazhan_blink_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_karazhan_blink_SpellScript);

        void HandleDummy(SpellEffIndex effIndex)
        {
            PreventHitDefaultEffect(effIndex);
            GetCaster()->getThreatManager().resetAllAggro();
            if (Unit* target = GetHitUnit())
                GetCaster()->CastSpell(target, SPELL_BLINK, true);
        }

        void Register()
        {
            OnEffectHitTarget += SpellEffectFn(spell_karazhan_blink_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
        }
    };

    SpellScript* GetSpellScript() const
    {
        return new spell_karazhan_blink_SpellScript();
    }
};


void AddSC_instance_karazhan()
{
    new instance_karazhan();
    new spell_karazhan_brittle_bones();
    new spell_karazhan_overload();
    new spell_karazhan_blink();
}
